import requests
import json
from typing import List, Dict, Any
from src.core.config import settings
from src.schemas.question import QuestionCreate, QuestionType
from pydantic import ValidationError  # Pydantic数据验证异常


class DifyClient:
    """
    Dify API客户端

    功能：调用Dify平台的大模型生成题目，封装API请求细节
    特性：自动处理请求格式、错误处理、响应解析
    """

    def __init__(self):
        """初始化客户端，验证配置"""
        # 从配置读取关键参数
        self.api_key = settings.dify_api_key
        self.base_url = settings.dify_base_url
        self.model_id = settings.dify_model_id

        # 验证配置是否完整
        if not self.api_key:
            raise ValueError("DIFY_API_KEY未配置，请在.env文件中设置")
        if not self.model_id:
            raise ValueError("DIFY_MODEL_ID未配置，请在.env文件中设置")

        # 请求头（固定格式）
        self.headers = {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json"
        }

    def _send_request(self, endpoint: str, payload: Dict[str, Any]) -> Dict[str, Any]:
        """
        发送HTTP请求到Dify API

        Args:
            endpoint: API端点（如"/v1/chat/completions"）
            payload: 请求体数据

        Returns:
            API响应的JSON数据

        Raises:
            RuntimeError: 请求失败或API返回错误
        """
        url = f"{self.base_url}{endpoint}"
        try:
            # 发送POST请求（Python 3.12的requests库完全支持）
            response = requests.post(
                url=url,
                headers=self.headers,
                json=payload,
                timeout=30  # 设置超时（AI生成可能需要较长时间）
            )

            # 检查HTTP状态码（非200则抛出异常）
            response.raise_for_status()

            # 解析响应JSON
            result = response.json()

            # 检查Dify返回的错误信息
            if "error" in result:
                raise RuntimeError(f"Dify API错误: {result['error']['message']}")

            return result

        except requests.exceptions.RequestException as e:
            # 捕获网络请求异常（连接超时、DNS错误等）
            raise RuntimeError(f"请求Dify API失败: {str(e)}") from e

    def generate_questions(self,
                           outline: str,
                           question_types: List[QuestionType],
                           counts: List[int],
                           difficulty_range: List[float],
                           major: str,
                           course: str) -> List[QuestionCreate]:
        """
        调用Dify生成题目

        Args:
            outline: 教学大纲文本
            question_types: 题型列表
            counts: 各题型数量
            difficulty_range: 难度范围
            major: 所属专业
            course: 所属课程

        Returns:
            生成的题目列表（已验证格式的QuestionCreate模型）
        """
        # 1. 构建提示词（Prompt Engineering）
        # 题型描述映射（让AI更清晰理解题型要求）
        type_descriptions = {
            "single_choice": "单选题（只有一个正确答案，提供A/B/C/D选项）",
            "fill_blank": "填空题（用下划线_表示空缺，如'Python的创始人是__'）",
            "short_answer": "简答题（需要简要文字回答，无需选项）"
        }

        # 生成题型要求文本
        type_requirements = []
        for q_type, count in zip(question_types, counts):
            type_requirements.append(f"- {type_descriptions[q_type]}：{count}道")

        # 完整提示词
        prompt = f"""
        请根据以下教学大纲生成题目，严格遵循格式要求：

        【教学大纲】
        {outline}

        【生成要求】
        1. 专业：{major}
        2. 课程：{course}
        3. 题型及数量：
        {'\n'.join(type_requirements)}
        4. 难度：{difficulty_range[0]}~{difficulty_range[1]}分（0-5分制）
        5. 输出格式：必须是JSON数组，每个元素包含：
           - content: 题目内容（字符串）
           - question_type: 题型（必须是{question_types}中的一个）
           - difficulty: 难度系数（符合上述范围）
           - answer: 标准答案（字符串）
           - analysis: 题目解析（可选）
           - knowledge_point: 知识点标签（可选，多个用逗号分隔）

        【注意】
        - 题目必须原创，避免重复和歧义
        - 严格按照JSON格式输出，不要添加额外文字
        """

        # 2. 调用Dify的聊天接口
        response = self._send_request(
            endpoint="/v1/chat/completions",
            payload={
                "model": self.model_id,
                "messages": [{"role": "user", "content": prompt}],
                "response_format": {"type": "json_object"}  # 强制返回JSON
            }
        )

        # 3. 解析并验证响应
        try:
            # 提取AI生成的内容（处理可能的Markdown格式）
            content = response["choices"][0]["message"]["content"].strip()

            # 去除可能的代码块标记（如```json ... ```）
            if content.startswith("```json") and content.endswith("```"):
                content = content[7:-3].strip()

            # 解析JSON
            generated_data = json.loads(content)

            # 验证是否为数组
            if not isinstance(generated_data, list):
                raise ValueError("Dify返回的不是数组格式")

            # 转换为QuestionCreate模型列表（自动验证数据格式）
            return [QuestionCreate(**item) for item in generated_data]

        except json.JSONDecodeError as e:
            raise RuntimeError(f"Dify返回内容不是有效的JSON: {str(e)}") from e
        except ValidationError as e:
            raise RuntimeError(f"Dify返回的数据格式不符合要求: {str(e)}") from e
        except Exception as e:
            raise RuntimeError(f"解析Dify响应失败: {str(e)}") from e
